
#include <unistd.h>             /* for gettimeofday(), getpid() */
#include <stdio.h>              /* for printf() */
#include <sys/ipc.h>            /* for all IPC function calls */
#include <sys/shm.h>            /* for shmget(), shmat(), shmctl() */
#include <sys/sem.h>            /* for semget(), semop(), semctl() */
#include <string.h>
#include "estruturas.h"

#define ERRO_DISCO_CHEIO

#define ARQUIVO_N_ABERTO

#define ARQUIVO_N_EXISTE

#define SEM_PERMISSAO

#define POSICAO_INVALIDA

#define EOF1

// Usado para imprimir as mensagens da funcao encontrar inode
#define INODE

// Usado para simular a funcao Requisição driver
#define ATENDER

//Usado para imprimir mensagens do Write
#define WRITE

//Usado para imprimir mensagens do Verificar Descritor
#define DESCRITOR

//Usado para imprimir mensagens do atualizar_inode_descritor
#define INODE_DESCRITOR

//Usado para imprimir mensagens do alocar_cluster
#define ALOCAR

//crinado memoria compartilhada do buffer do SO
#define MSG_MEM_BUFFER


int carregar_inode( int numero_inode );
int requisicao_driver(Requisicao_cid req_cid);
int encontrar_inode(long int posicao_inode, long int endereco , int operacao);
int tratar_erros( int status , long int cluster );
long int alocar_cluster(int posicao_inode, int alocados);
int ffwrite(int descritor, char* buffer, int tamanho, int posicao);
long int retirar_cluster();
int verifica_descritor(int descritor);
void atualizar_inode_descritor(int alocados, int descritor);
  


/*

***********************************************************************
* Códigos dos Retornos dos Erros

 -1 (Arquivo não aberto)
 -2 (Posição para escrita inválida)
 -3 (Arquivo não existente)
 -4 (Acesso negado, o usuário não possui permissão)
 -5 Disco Cheio (Não há espaço para gravação)
 -7 EOF1
**********************************************************************
*/

/*
 * Variaveis Globais
 */

long int num_cluster;
Inode inode_aux; 

char * cluster;

int long clusters_double[2048];
int long clusters_single[2048];


//Funcao aloca cluster no inode usando o posicao_inode passado por parametro e ele retorna o numero do cluster novo
long int alocar_cluster(int posicao_inode, int alocados)
{
	long int num_cluster,num_cluster_single, num_cluster_d ;
	int dif, n_triple, n_double, n_single, i, mem_buffer;
	int status;
	Requisicao_cid requisicao;
	char* buffer_so;

		//associar buffer_so da memoria compartilhada
		mem_buffer = shmget( descritor_aux.buffer, sizeof(char *), 0);
		buffer_so = (char *) shmat(mem_buffer, NULL, 0);
		
		for(i=0; i<1024; i++) buffer_so[i] = ' ';

		//10 direto
		if(posicao_inode >=0 && posicao_inode <=9)
		{
			#ifdef ALOCAR
			printf("\nEntrou no Single do Alocar Cluster \n");
			fflush(stdout);
			#endif
			//Retirar um numero de cluster da lista de clusters livres
			num_cluster = retirar_cluster();
			#ifdef ERRO_DISCO_CHEIO
			//Erro de Disco Cheio
			if (num_cluster < 0) return -5;
			#endif
			//Inserir cluster na posicao do inode
			inode_aux.direto[posicao_inode] = num_cluster;
	
			alocados++;
			
			return num_cluster;
		}
		
		//Single
		if(posicao_inode>=10 && posicao_inode <=2047)
		{
			//C�lculo da posicao dentro do Single 
			n_single =  posicao_inode - 10;

			//Alocar primeira posicao do Single
			if (posicao_inode == 10)
			{
			#ifdef ALOCAR
			printf("\nEntrou no Single Primeira Posicao do Alocar Cluster\n");
			fflush(stdout);
			#endif
			//Retirar um numero de cluster da lista de clusters livres para o single
			num_cluster_single = retirar_cluster();
			
			#ifdef ERRO_DISCO_CHEIO
			//Erro de Disco Cheio
			if (num_cluster_single < 0) return -5;
			#endif
			//Inserir cluster na posicao do inode (Single)
			inode_aux.single = num_cluster_single;

			#ifdef ALOCAR
			printf("\nCluster do Single: %i", num_cluster_single);
			fflush(stdout);
			#endif

			//Construir Single
			//Loop p�e EOF1(-1) em todas posicoes
			for(i = 0; i< 2048; i++)
			{
			clusters_single[i] = -1; 
			}
			
			//Retirar um numero de cluster da lista de clusters livres
			//num_cluster guarda numero do cluster do primeiro do Single
			num_cluster = retirar_cluster();
			#ifdef ERRO_DISCO_CHEIO
			//Erro de Disco Cheio
			if (num_cluster < 0) return -5;
			#endif
			//Aloca primeira posicao no Single
			clusters_single[n_single] = num_cluster;

			#ifdef ALOCAR
			printf("\nPrimeiro Cluster do Single: %i", num_cluster);
			fflush(stdout);
			#endif
			//Escrever Single no Disco
			requisicao.numero_cluster = num_cluster_single;
			requisicao.tipo_operacao = 1;
			requisicao.pid = getpid();
			requisicao.numero_bytes = 1024;
			requisicao.endereco = (long int) &clusters_single;
		
			//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
			status =  requisicao_driver(requisicao);
			
			//Erro de Driver
			if(status < 0) return status;

			alocados = alocados + 2;
			return num_cluster;

			}

			//Alocar outros clusters depois da primeira posicao do Single
			else{
				//Carregar Single
				num_cluster_single =  inode_aux.single;
				#ifdef ALOCAR
				printf("\nNumero Cluster Single: %i", num_cluster_single);
				fflush(stdout);
				#endif

				//Carregar Single na memoria
				requisicao.numero_cluster = num_cluster_single;
				requisicao.tipo_operacao = 0;
				requisicao.pid = getpid();
				requisicao.numero_bytes = 1024;
				requisicao.endereco = (long int) &clusters_single;
		
				//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
				status =  requisicao_driver(requisicao);
				if (status < 0 ) 
				{
					return status;
				}

				//Retirar um numero de cluster da lista de clusters livres
				//num_cluster guarda numero do cluster do primeiro do Single
				num_cluster = retirar_cluster();
				#ifdef ERRO_DISCO_CHEIO
				//Erro de Disco Cheio
				if (num_cluster < 0) return -5;
				#endif
				//Aloca cluster na posicao no Single
				clusters_single[n_single] = num_cluster;

				//Escrever Single no Disco
				requisicao.numero_cluster = num_cluster_single;
				requisicao.tipo_operacao = 1;
				requisicao.pid = getpid();
				requisicao.numero_bytes = 1024;
				requisicao.endereco = (long int) &clusters_single;
		
				//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
				status =  requisicao_driver(requisicao);
				if (status < 0 ) 
				{
					return status;
				}
				alocados++;
				return num_cluster;

			}

		}

	//Double 
	if(posicao_inode >=2048 && posicao_inode <= 4194304)
	{
		#ifdef ALOCAR
		printf("\nEntrou no Double do Alocar Cluster");
		fflush(stdout);
		#endif

		//C�lculo dos �ndices
		dif = posicao_inode - 2048;
		n_double = dif / 2048;
		n_single = dif % 2048;
		#ifdef ALOCAR
		printf("\nResultado Dif: %i", dif);
		fflush(stdout);
		printf("\nResultado n_double: %i", n_double);
		fflush(stdout);
		printf("\nResultado n_single: %i", n_single);
		fflush(stdout);
		#endif

			//Alocar Primeira posicao do Double 
			if(posicao_inode == 2048)
			{
				#ifdef ALOCAR
				printf("\nEntrou no Primeiro do Double");
				fflush(stdout);
				#endif

				//Retirar um numero de cluster da lista de clusters livres para o Double
				//num_cluster_d guarda numero de cluster do Double
				num_cluster_d = retirar_cluster();
				#ifdef ERRO_DISCO_CHEIO
				//Erro de Disco Cheio
				if (num_cluster_d < 0) return -5;
				#endif
				//Inserir cluster na posicao do inode (Double)
				inode_aux.doubles = num_cluster_d;

				#ifdef ALOCAR
				printf("\nCluster do double: %i", num_cluster_d);
				fflush(stdout);
				#endif

				//Construir Double
				//Loop p�e EOF1(-1) em todas posicoes
				for(i = 0; i< 2048; i++)
				{
				clusters_double[i] = -1; 
				}
			
				//Retirar um numero de cluster da lista de clusters livres
				//num_cluster guarda numero do cluster do primeiro do Single
				num_cluster_single = retirar_cluster();
				#ifdef ERRO_DISCO_CHEIO
				//Erro de Disco Cheio
				if (num_cluster_single < 0) return -5;
				#endif
				//Aloca primeira posicao no Double
				//num_cluster_d2 tem o numero do cluster do Single
				clusters_double[n_double] = num_cluster_single;

			
				//Escrever Double no Disco
				requisicao.numero_cluster = num_cluster_d;
				requisicao.tipo_operacao = 1;
				requisicao.pid = getpid();
				requisicao.numero_bytes = 1024;
				requisicao.endereco = (long int) &clusters_double;
		
				//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
				status =  requisicao_driver(requisicao);

				//Erro de Driver
				if(status < 0) return status;
			
				//Retirar um numero de cluster da lista de clusters livres
				//num_cluster guarda numero de cluster para p�r no Single
				num_cluster = retirar_cluster();
				#ifdef ERRO_DISCO_CHEIO
				//Erro de Disco Cheio
				if (num_cluster < 0) return -5;
				#endif
				//Construir Double
				//Loop p�e EOF1(-1) em todas posicoes
				for(i = 0; i< 2048; i++)
				{
				clusters_single[i] = -1; 
				}
				
				clusters_single[n_single] = num_cluster;

				#ifdef ALOCAR
				printf("\nPrimeiro Cluster do Double: %i", num_cluster_single);
				fflush(stdout);
				printf("\nPrimeiro Cluster do Single: %i", num_cluster);
				fflush(stdout);
				#endif

				//Escrever Single no Disco
				requisicao.numero_cluster = num_cluster_single;
				requisicao.tipo_operacao = 1;
				requisicao.pid = getpid();
				requisicao.numero_bytes = 1024;
				requisicao.endereco = (long int) &clusters_single;
		
				//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
				status =  requisicao_driver(requisicao);

				//Erro de Driver
				if(status < 0) return status;

				alocados = alocados+3;
				return num_cluster;
				
			}
			//Alocar os demais clusters do Double
			else
			{
				//Primeiro Single
				if(n_single == 0)
				{	
					#ifdef ALOCAR
					printf("\nEntrou no Double com primeiro Single");
					fflush(stdout);
					#endif
					//Pegar endere�o do Double 
					num_cluster_d = inode_aux.doubles;
					
					//Carregar Double na memoria
					requisicao.numero_cluster = num_cluster_d;
					requisicao.tipo_operacao = 0;
					requisicao.pid = getpid();
					requisicao.numero_bytes = 1024;
					requisicao.endereco = (long int) &clusters_double;
					
					//Mandar Requisi��o CID
					status =  requisicao_driver(requisicao);

					if(status < 0) return status;
		
					//Retirar cluster para Double
					//num_cluster_d guarda numero de cluster do Double
					num_cluster_single = retirar_cluster();
					#ifdef ERRO_DISCO_CHEIO
					//Erro de Disco Cheio
					if (num_cluster_single < 0) return -5;
					#endif
					//Alocar novo numero de cluster no double
					clusters_double[n_double] = num_cluster_single;

					//Descarregar Double no disco
					requisicao.numero_cluster = num_cluster_d;
					requisicao.tipo_operacao = 1;
					requisicao.pid = getpid();
					requisicao.numero_bytes = 1024;
					requisicao.endereco = (long int) &clusters_double;

					//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
					status =  requisicao_driver(requisicao);

					//Erro de Driver
					if(status < 0) return status;

					//Retirar um numero de cluster da lista de clusters livres
					//num_cluster guarda numero de cluster para p�r no Single
					num_cluster = retirar_cluster();
					#ifdef ERRO_DISCO_CHEIO
					//Erro de Disco Cheio
					if (num_cluster < 0) return -5;
					#endif
					//Construir Double
					//Loop p�e EOF1(-1) em todas posicoes
					for(i = 0; i< 256; i++)
					{
					clusters_single[i] = -1; 
					}
					
					clusters_single[n_single] = num_cluster;

					//Descarregar Single no disco
					requisicao.numero_cluster = num_cluster_single;
					requisicao.tipo_operacao = 1;
					requisicao.pid = getpid();
					requisicao.numero_bytes = 1024;
					requisicao.endereco = (long int) &clusters_single;

					//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
					status =  requisicao_driver(requisicao);

					//Erro de Driver
					if(status < 0) return status;

					alocados = alocados+2;
					return num_cluster;

				}
			
				//Alocar os demais clusters
				if(n_single >0)
				{
					#ifdef ALOCAR
					printf("\nEntrou no Double, Single Completo");
					fflush(stdout);
					#endif

					//Pegar endere�o do Double 
					num_cluster_d = inode_aux.doubles;
					
					//Carregar Double na memoria
					requisicao.numero_cluster = num_cluster_d;
					requisicao.tipo_operacao = 0;
					requisicao.pid = getpid();
					requisicao.numero_bytes = 1024;
					requisicao.endereco = (long int) &clusters_double;
				
					//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
					status =  requisicao_driver(requisicao);
					if (status < 0 ) 
					{
						return status;
					}
				
					//Guardar numero do cluster do Single no num_cluster_single
					num_cluster_single = clusters_double[n_double];
				
					//Carregar Single na Memoria
					requisicao.numero_cluster = num_cluster_single;
					requisicao.tipo_operacao = 0;
					requisicao.pid = getpid();
					requisicao.numero_bytes = 1024;
					requisicao.endereco = (long int) &clusters_single;

					//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
					status =  requisicao_driver(requisicao);
					if (status < 0 ) 
					{
						return status;
					}

				
					//Retirar um numero de cluster da lista de clusters livres
					//num_cluster guarda numero de cluster para p�r no Single
					num_cluster = retirar_cluster();
					#ifdef ERRO_DISCO_CHEIO
					//Erro de Disco Cheio
					if (num_cluster < 0) return -5;
					#endif
					clusters_single[n_single] = num_cluster;

					//Descarregar Single no disco
					requisicao.numero_cluster = num_cluster_single;
					requisicao.tipo_operacao = 1;
					requisicao.pid = getpid();
					requisicao.numero_bytes = 1024;
					requisicao.endereco = (long int) &clusters_single;

					//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
					status =  requisicao_driver(requisicao);

					//Erro de Driver
					if(status < 0) return status;
				
					#ifdef ALOCAR
					printf("\nCluster do Double: %i", num_cluster_d);
					fflush(stdout);
					printf("\nCluster do Double para Single: %i", num_cluster_single);
					fflush(stdout);
					printf("\nCluster do Single: %i", num_cluster);
					fflush(stdout);
					#endif

					alocados = alocados+1;
					return num_cluster;
				}	
			}
	}

}
//Funcao para Write. Ela percorre o inode de acordo com o posicao_inode, 
//Carrega o cluster no endere�o passado e retorna o numero do cluster. E
//Retorna o valor -7 quando h� o erro de EOF1.
int encontrar_inode(long int posicao_inode, long int endereco, int operacao)
{
 Requisicao_cid requisicao;
 long int num_cluster;
 int dif, n_single, n_double, n_triple;
 int status;

	//Primeiras 10 posicoes do Inode
	if(posicao_inode >= 0 && posicao_inode < 10)
	{
		#ifdef INODE
		printf("Procurando nos Primeiros Inodes, posicao: [%d]\n",posicao_inode);
		#endif

		num_cluster = inode_aux.direto[posicao_inode];
		#ifdef EOF1
		//Posicao do cluster passado esta vazia(EOF1)=> erro de EOF1 = -7
		if(num_cluster < 0 ) return -7; 
		#endif
		
		if(operacao ==1)
		{
			//Montar Requisicao CID
			requisicao.numero_cluster = num_cluster;
			requisicao.tipo_operacao = 0;
			requisicao.pid = getpid();
			requisicao.numero_bytes = 1024;
			requisicao.endereco = endereco;
			
			//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
			status =  requisicao_driver(requisicao);
			if (status < 0 ) return status;
		}
		//Sucesso
		return num_cluster;
	}
	
	//Single
	if(posicao_inode >=10 && posicao_inode <=2047)
	{
		#ifdef INODE
		printf("Procurando no Single, posicao:[%d]\n",posicao_inode);
		#endif

		//Carregar Single
		num_cluster = inode_aux.single;
		#ifdef EOF1
		//O Single esta vazia(EOF1)=> erro de EOF1 = -7
		if(num_cluster <0) return -7;
		#endif
		
		//Montar Requisicao CID para carregar Single
		requisicao.numero_cluster = num_cluster;
		requisicao.tipo_operacao = 0;
		requisicao.pid = getpid();
		requisicao.numero_bytes = 1024;
		requisicao.endereco = (long int) &clusters_single;
		
		//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
		status =  requisicao_driver(requisicao);
		if (status < 0 ) return status;
		
		//Calculo da posicao do indice do inode no single
		n_single = posicao_inode - 10;
		num_cluster = clusters_single[n_single];

		#ifdef INODE
		printf("Numero posicao Single:  %i\n", n_single);
		#endif
		
		#ifdef EOF1
		//Posicao do Single esta vazia(EOF1)=> erro de EOF1 = -7
		if(num_cluster < 0) return -7;
		#endif
		
		if(operacao ==1)
		{
			//Montar Requisicao CID para carregar cluster dentro do Single
			requisicao.numero_cluster = num_cluster;
			requisicao.tipo_operacao = 0;
			requisicao.pid = getpid();
			requisicao.numero_bytes = 1024;
			requisicao.endereco = endereco;
	
			//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
			status =  requisicao_driver(requisicao);
			if (status <0 ) return status;
		}
		//Sucesso
		return num_cluster;
	}

	//Double 
	if(posicao_inode >=2048 && posicao_inode <= 4194307)
	{
		#ifdef INODE
		printf("Procurando no Double, posicao: [%d]\n",posicao_inode);
		#endif

		//Carregar Double
		num_cluster = inode_aux.doubles;
		#ifdef EOF1
		//O Double esta vazia(EOF1)=> erro de EOF1 = -7
		if(num_cluster <0) return -7;
		#endif		
		//Montar Requisicao CID para carregar Double
		requisicao.numero_cluster = num_cluster;
		requisicao.tipo_operacao = 0;
		requisicao.pid = getpid();
		requisicao.numero_bytes = 1024;
		requisicao.endereco = (long int) &clusters_double;
		
		//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
		status =  requisicao_driver(requisicao);
		if (status < 0 ) return status;
		
		
		//Diferenca do indice do inode e 2048
		dif = posicao_inode - 2048;
		n_double = dif / 2048;

		#ifdef INODE
		printf("Resultado Dif: %i\n", dif);
		printf("Resultado Double: %i\n", n_double);
		#endif
		num_cluster = clusters_double[n_double];
		
		#ifdef EOF1
		//A posicao do Double esta vazia(EOF1)=> erro de EOF1 = -7
		if(num_cluster <0) return -7;
		#endif
		
		//Montar Requisicao CID para carregar Single
		requisicao.numero_cluster = num_cluster;
		requisicao.tipo_operacao = 0;
		requisicao.pid = getpid();
		requisicao.numero_bytes = 1024;
		requisicao.endereco = (long int) &clusters_single;
		
		//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
		status =  requisicao_driver(requisicao);
		if (status < 0 ) return status;
		
		
		//Calculo da posicao dentro do Single
		n_single = dif % 2048;
		num_cluster = clusters_single[n_single];

		#ifdef INODE
		printf("Resultado Single: %i\n", n_single);
		#endif
		
		#ifdef EOF1
		//A posicao do Double esta vazia(EOF1)=> erro de EOF1 = -7
		if(num_cluster <0) return -7;
		#endif
		
		if(operacao ==1)
		{
			//Montar Requisicao CID para carregar cluster dentro do Single
			requisicao.numero_cluster = num_cluster;
			requisicao.tipo_operacao = 0;
			requisicao.pid = getpid();
			requisicao.numero_bytes = 1024;
			requisicao.endereco = endereco;
	
			//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
			status =  requisicao_driver(requisicao);
			if (status <0 ) return status;
		}

		//Sucesso
		return num_cluster;
	}

	
}

// Funcao que faz o tratamento dos erros que sao passados do driver para a CID
// Essa funcao � chamada pela atender driver e retorna o status para a atender driver essa
// que encaminha o erro para as funcao write
int tratar_erros( int status , long int cluster )
{
	int i;
	int mem_superbloco_id;
	char * mem_superbloco_addr;
	Superbloco * sb;
	
	switch(status)
	{
	// Sucesso
	case -5:
		/*- Inserir numero de Cluster Danificado dentro da Lista de Clusters Danificados na
		memoria compartilhada. */
		mem_superbloco_id = shmget( MEM_SUPERBLOCO, sizeof(Superbloco), 0 );
		mem_superbloco_addr = shmat(mem_superbloco_id, NULL, 0);

		// Trava o semaforo do superbloco
		semop( g_sem_super_id, g_lock_sembuf, 1 );

		sb = (Superbloco *) mem_superbloco_addr;
		
		i = 0;
		while(sb->clusters_danificados[i]!=-1) i++;
		
		sb->clusters_danificados[i] = cluster;

		// Livera o semaforo superbloco
		semop( g_sem_super_id, g_unlock_sembuf, 1 );

		return -5;
	break;

	
	default:
		return status;
	break;	
		
	}
}


//Funcao que manda Requisicoes CID ao Driver 

int requisicao_driver(Requisicao_cid requisicao)
{	
	
int erro;
int status=1;
		erro=tratar_erros(status,120);

	return erro;
	
}


//Funcao usada pelo alocar cluster para retirar 
//um numero de cluster na lista de clusters livres.
long int retirar_cluster()
{
 int i;
 int indice = 184;
 long num_cluster;
 long int primeiro;
 long int ultimo; 
	

	//Variavel que auxiliar na manipulacao do superbloco
	Superbloco *super;

	//Identificacao da Memoria Compartilhada do PCB
	int mem_super;

	//Semaforo que protege a memoria compartilhada do Superbloco
	//P(semaforo_super)
	if( semop( g_sem_super_id, g_lock_sembuf, 1 ) == -1 )
        {
	 fprintf(stderr,"chamada semop() falhou, impossivel fechar o semaforo!");
         exit(1);
        }

		//Associa a memoria compartilhada do PCB
		mem_super = shmget( MEM_SUPERBLOCO, sizeof(Superbloco), 0);
		super = (Superbloco*) shmat(mem_super, NULL, 0); 
		super->clusters_livres[184]=-1; 
 		//Verifica qual � o primeiro intervalo
		 for(i =0; i<=184; i++)
		{
			if(super->clusters_livres[i] != -1) 
			{
			indice = i;
			break;
			}
		}
		#ifdef ERRO_DISCO_CHEIO
		//Sem Clusters Livres(disco cheio)
		if (indice >=184) return - 5;
		#endif
		//Verifica se primeiro e �ltimo do intervalo s�o iguais
		//Caso sejam guarda o numero cluster em num_cluster
		// e p�e -1 em ambas posicoes. 
		primeiro = super->clusters_livres[indice];
		ultimo = super->clusters_livres[indice+1];
		if (primeiro == ultimo)
		{
		num_cluster = super->clusters_livres[indice];
		super->clusters_livres[indice] =-1;
		super->clusters_livres[indice+1] =-1;
		}
		//Sen�o decrementa o numero do cluster pois ele vai ser retirado
		else {
		num_cluster = super->clusters_livres[indice];
		super->clusters_livres[indice] = super->clusters_livres[indice] + 1;
		}
	
	//V(semaforo_super)
	if( semop( g_sem_super_id, g_unlock_sembuf, 1 ) == -1 )
        {
         fprintf(stderr,"chamada semop() falhou, impossivel liberar o semaforo!");
         exit(1);
        }
	
	return num_cluster;
}


//Funcao verifica se existe o descritor passado dentro do PCB. Caso existir, copia descritor 
 //uma variavel global. 
//Depois disso, percorre a Tabela de arquivo aberto para achar inode correspondente ao indice 
//dentro do descritor. 
//Uma vez achado, copia inode em um inode global.
int verifica_descritor(int descritor)
{
	int i;

	//Vairavel que copia indice de inode do descritor
	No_taa* laa_ptr;

	//Identificacao da Memoria Compartilhada do No_taa
	int mem_chave;

	//Variavel auxiliar que percorre a lista ligada da (Tabela de Arquivos Abertos).
	No_taa *etaa;

	//Variavel que atualiza na manipulacao do PCB
	Pcb *pcb;

	//Identificacao da Memoria Compartilhada do PCB
	int mem_pcb;

	//Associa a memoria compartilhada do PCB
	mem_pcb = shmget( MEM_PCB, sizeof(Pcb), 0);
	pcb = (Pcb*) shmat(mem_pcb, NULL, 0); 
	
	#ifdef ARQUIVO_N_EXISTE
	//Descritor Negativo (Erro de Descritor Inexistente)
	if(descritor <0 || descritor > 9) return -3;
	#endif
	
	#ifdef SEM_PERMISSAO
	//Arquivo sem permissao de escrita
	if(pcb->vetor_descritores[descritor].modo_abertura == 1 )
	  return -4;
	#endif
	
	//Verifica se o descritor passado por parametro existe no vetor de descritores
	//Faz isso pegando o indice do inode da posicao do descritor
	laa_ptr = (No_taa*)pcb->vetor_descritores[descritor].laa_ptr;
	#ifdef DESCRITOR
	printf("\nIndice Inode recebido: %i", laa_ptr);
	#endif
	
	#ifdef ARQUIVO_N_EXISTE
	//Caso o descritor nao existir e retornado o valor -3 (Erro de Descritor Inexsitente)
	if(laa_ptr <0) return -3; 
		//else return 0;
	#endif
	
	//Copiar descritor do vetor para descritor global
	descritor_aux.ponteiro_arq = pcb->vetor_descritores[descritor].ponteiro_arq;
	descritor_aux.laa_ptr = pcb->vetor_descritores[descritor].laa_ptr;
	descritor_aux.buffer = pcb->vetor_descritores[descritor].buffer;
	descritor_aux.modo_abertura = pcb->vetor_descritores[descritor].modo_abertura;
	descritor_aux.aberto = pcb->vetor_descritores[descritor].aberto;

	//arquivo n'ao aberto
	if( descritor_aux.modo_abertura <= 0 )
		return -5;


	#ifdef DESCRITOR
	printf("\nIndice Descritor Auxiliar: %i\n", descritor_aux.laa_ptr);
	fflush(stdout);
	#endif

	//Associa a memoria compartilhada da  Estrutura TAA
	mem_chave = shmget( MEM_TAA, sizeof(No_taa), 0);
	etaa = (No_taa*) shmat(mem_chave, NULL, 0);

	//Semaforo que protege a memoria compartilhada da Tabela de Arquivo Abertos
	//P(semaforo_taa)
	if( semop( g_sem_taa_id, g_lock_sembuf, 1 ) == -1 )
        {
	           fprintf(stderr,"chamada semop() falhou, impossivel fechar o semaforo!");
                   exit(1);
        }
		//Percorre Tabela de Arquivos Abertos
			
			#ifdef DESCRITOR
			printf("\nEntrou na Tabela de Arquivos Abertos..."); 
			fflush(stdout);
			printf("\nIndice do Inode: %i", etaa->i_node.idi_node);
			fflush(stdout);
			#endif
			
		
				//Copiar inode do no no indoe_Auxiliar
				#ifdef DESCRITOR
				printf("\nIndice do Inode Encontrado na TAA: %i", etaa->i_node.idi_node);
				fflush(stdout);
				#endif

				inode_aux.criacao = etaa->i_node.criacao;
				inode_aux.ultima_modificacao = etaa->i_node.ultima_modificacao;
				inode_aux.tamanho = etaa->i_node.tamanho;
				inode_aux.idi_node = etaa->i_node.idi_node;
				for(i= 0; i< 10; i++)	inode_aux.direto[i] = etaa->i_node.direto[i];
				inode_aux.single =  etaa->i_node.single;
				inode_aux.doubles =  etaa->i_node.doubles;
			
				

	//V(semaforo_taa)
	if( semop( g_sem_taa_id, g_unlock_sembuf, 1 ) == -1 )
        {
                   fprintf(stderr,"chamada semop() falhou, impossivel liberar o semaforo!");
                   exit(1);
        }
	
	//Retorna 0: Sucesso	
	return 0;	
}


//Funcao atualiza descritor no vetor de descritores do PCB e inode na Tabela de Arquivos Abertos
void atualizar_inode_descritor (int alocados, int descritor)
{
	No_taa *etaa;
	Pcb * pcb;
	int i, mem_chave, mem_pcb ;
		#ifdef INODE_DESCRITOR
		printf("\nAtualizando Descritor e Inode...\n");
		fflush(stdout);
		#endif

		//Atualizar Inode na Tabela de Arquivos Abertos
		//Associa a memoria compartilhada da TAA
		mem_chave = shmget( MEM_TAA, sizeof(No_taa), 0);
		etaa = (No_taa*) shmat(mem_chave, NULL, 0);

	
			etaa->i_node.criacao = inode_aux.criacao;
			etaa->i_node.ultima_modificacao = inode_aux.ultima_modificacao;
			etaa->i_node.tamanho = inode_aux.tamanho + alocados*1024;
			etaa->i_node.idi_node = inode_aux.idi_node;
			for(i= 0; i< 10; i++) etaa->i_node.direto[i] = inode_aux.direto[i];
			etaa->i_node.doubles = inode_aux.doubles;
						

		//Atualizar descritor no PCB
		//Associa a memoria compartilhada do PCB
		mem_pcb = shmget( MEM_PCB, sizeof(Pcb), 0);
		pcb = (Pcb*) shmat(mem_pcb, NULL, 0);

		pcb->vetor_descritores[descritor] = descritor_aux;
}

int ffwrite(int descritor, char* buffer, int tamanho, int posicao)
{
	long int status;
	int indice_clusters;
	int posicao_inode;
	int posicao_passada;
	int indice_buffer;
	long int num_cluster;
	int i, alocados = 0, j=0, k;	
	int mem_buffer;
	char *buffer_so;
	int end;
	Requisicao_cid requisicao;

	//Chamar verifica_descritor para verificar descritor e copiar inode no inode_aux
	status = verifica_descritor(descritor);

	//Erro de Descritor Invalido ou Flag
	if (status < 0) return status;
	
	//Verificar se posicao esta no parametro do arquivo
	//Variavel que ultimo indice do inode 
	indice_clusters = (inode_aux.tamanho/1024) - 1;
	
	#ifdef POSICAO_INVALIDA
	//Verificar se a posicao EH negativa
	//Se for, erro de Posicao Inv�lida
	if (posicao <0) return -2;
	#endif
	
	//Calculo do indice do inode com posicao passada
	posicao_passada = posicao / 1024;

	#ifdef WRITE
	printf("\nPosicao Recebida: %i", posicao_passada);
	fflush(stdout);
	printf("\nIndice clusters: %i", indice_clusters);
	fflush(stdout);
	#endif
	
	#ifdef POSICAO_INVALIDA
	//Erro de EOF1
	if(posicao_passada > indice_clusters) return -2;
	#endif
	
	//Variavel que guarda indice de posicao dentro do inode
	posicao_inode = descritor_aux.ponteiro_arq/1024;

	//Associar a memoria compartilhada do buffer do SO
	mem_buffer = shmget( descritor_aux.buffer, sizeof(char *), 0);
	buffer_so = (char *) shmat(mem_buffer, NULL, 0);

	#ifdef WRITE
	printf("\nPosicao no inode onde está Ponteiro do Arquivo: %i", posicao_inode);
	fflush(stdout);
	#endif
	
	#ifdef ARQUIVO_N_ABERTO
	//Arquivo nao aberto
	if(descritor_aux.aberto == 0)
	{
		return -1;
	}

	#endif

	//Se a posicao passada for maior que o ponteiro do Arquivo:
	//Descarregar buffer do so no disco e carregar outro na memoria
	if(posicao_passada !=  (descritor_aux.ponteiro_arq/1024))
	{
	
		//Pegar numero do cluster no inode
		num_cluster = encontrar_inode (posicao_inode, (long int) &end, 0);
		printf("\nNumero do Cluster: %i", num_cluster);
		
		//Erro de Driver
		if(num_cluster<0) return num_cluster;
	
		//Escrever buffer no Disco
		requisicao.numero_cluster = num_cluster;
		requisicao.tipo_operacao = 1;
		requisicao.pid = getpid();
		requisicao.numero_bytes = 1024;
		requisicao.endereco = (long int) &buffer_so;
	
		status = requisicao_driver(requisicao);
	
		//Erro de Driver
		if(status <0) return status;
	
		//Carregar na memoria o buffer do so
		num_cluster = encontrar_inode (posicao_passada, (long int) &buffer_so, 1);
	
		//Erro de Driver
		if(num_cluster < 0) return num_cluster;
	}

		

	else 
	{
		

		//Variavel que tem indice do buffer_so
		indice_buffer = descritor_aux.ponteiro_arq % 1024;
	
		#ifdef WRITE
		printf("\nIndice Buffer SO: %i", indice_buffer);
		fflush(stdout);
		#endif 
		
	
		#ifdef WRITE
		printf("\nPosicao do Inode: %i", posicao_inode);
		fflush(stdout);
		#endif
	
		//Semaforo que protege a memoria compartilhada da Tabela de Arquivo Abertos
		//P(semaforo_taa)
		if( semop( g_sem_taa_id, g_lock_sembuf, 1 ) == -1 )
		{
			fprintf(stderr,"chamada semop() falhou, impossivel fechar o semaforo!");
			exit(1);
		}
		
		//Loop que escreve no buffer_SO
		for(i =0; i < tamanho; i++)
		{
			buffer_so[indice_buffer] = buffer[i];
			indice_buffer ++;
			j= i+1;
			//Buffer Cheio
			if(indice_buffer == 1024 && j < tamanho)
			{
				indice_buffer = 0;

				//Escrever buffer no Disco
				requisicao.numero_cluster = num_cluster;
				requisicao.tipo_operacao = 1;
				requisicao.pid = getpid();
				requisicao.numero_bytes = 1024;
				requisicao.endereco = (long int) &buffer_so;
				#ifdef WRITE
				printf("\n Buffer SO sendo escrito no Disco: %s", buffer_so);fflush(stdout);
				#endif
				
				//Enviar Requisicao CID para o Driver pela funcao Requisicao driver 
				status =  requisicao_driver(requisicao);

				if(status<0) return status;
				
				
				//Incrementar o posicao_inode
				posicao_inode++;
				#ifdef WRITE
				printf("\nProximo Indice do Inode: %i", posicao_inode);
				fflush(stdout);
				#endif
				
				status = encontrar_inode(posicao_inode, (long int) &buffer_so, 1);
				
				if(status <0 )
				{
					if(status==-7) {
						num_cluster = alocar_cluster(posicao_inode, alocados); 
						if(num_cluster<0) {
							//Erro
							//Atualizar Ponteiro de Arquivo
							descritor_aux.ponteiro_arq = descritor_aux.ponteiro_arq + i +1;
							//Atualizar Inode
							atualizar_inode_descritor(alocados, descritor);
							//V(semaforo_taa)
							if( semop( g_sem_taa_id, g_unlock_sembuf, 1 ) == -1 )
      						 	{
        							fprintf(stderr,"chamada semop() falhou, impossivel liberar o semaforo!");
        							exit(1);
	      						}
							return num_cluster;
						} 
						else { 
						#ifdef WRITE
						printf("\nNumero do novo Cluster: %i", num_cluster);
						fflush(stdout);
						#endif
						}
					}else{
						//Atualizar Ponteiro de Arquivo
						descritor_aux.ponteiro_arq = descritor_aux.ponteiro_arq + i +1;
						//Atualizar Inode
						atualizar_inode_descritor(alocados, descritor);

						if( semop( g_sem_taa_id, g_unlock_sembuf, 1 ) == -1 )
      						{
        						fprintf(stderr,"chamada semop() falhou, impossivel liberar o semaforo!");
        						exit(1);
	      					}
						printf("\nstatus : %i", status);
						return status;	
					} 
				} else num_cluster = status;
				#ifdef WRITE
				printf("\nNumero do Cluster do loop: %i", num_cluster);
				fflush(stdout);
				#endif
		
	
			}
		}	

		//Atualizar Ponteiro de Arquivo
		descritor_aux.ponteiro_arq = descritor_aux.ponteiro_arq + tamanho;
		
		#ifdef WRITE
		printf("\nPonteiro do Arquivo Atual: %i\n", descritor_aux.ponteiro_arq);
		fflush(stdout);
		printf("\nIndice buffer Atual: %i", indice_buffer);
		fflush(stdout);
		#endif

		//Atulaizar Inode na TAA e descritor no PCB
		atualizar_inode_descritor(alocados, descritor);
		#ifdef WRITE
		printf("\nBuffer_so apos escrita no Disco:%s", buffer_so);fflush(stdout);
		#endif
		
			
		//V(semaforo_taa)
		if( semop( g_sem_taa_id, g_unlock_sembuf, 1 ) == -1 )
		{
			fprintf(stderr,"chamada semop() falhou, impossivel liberar o semaforo!");
			exit(1);
		}
	
		
		return 0;
	}

	
	atualizar_inode_descritor(alocados, descritor);

	//return status;
	
}


void inicializar()
{
	No_taa *etaa, *novo;
	Pcb *pcb_ptr;
	Superbloco *super;
	int mem_taa, i, mem_pcb, mem_super, mem_novo, mem_buffer;
	char* novo_addr, novo_addr2, *buffer_so;

	

	/*
	 * Criando a memoria compartilhada Buffer_SO
	 */
	mem_buffer_id = shmget( MEM_BUFFER, sizeof(char*), IPC_CREAT | 0666);
	mem_buffer_addr = shmat(mem_buffer_id, NULL, 0);

	#ifdef MSG_MEM_BUFFER
	printf("Criando memoria compartilhada do Buffer SO\n");
	#endif
	init();

	//Associa a mem�ria compartilhada do TAA
	mem_taa = shmget( MEM_TAA, sizeof(No_taa), 0);
	etaa = (No_taa*) shmat(mem_taa, NULL, 0);

	etaa->quant_proc_acessando = 1;
	etaa->i_node.criacao = 5;
	etaa->i_node.ultima_modificacao = 5;
	etaa->i_node.idi_node = 4;
	etaa->i_node.tamanho = 1024;
	
		
	for(i=0; i<10; i++)
	{
		etaa->i_node.direto[i]= -1;
	}
	etaa->i_node.direto[0] = 150;
	etaa->i_node.single = -1;
	etaa->i_node.doubles = -1;
	etaa->i_node.triple = -1;


	key_taa++;
	
	if( (mem_novo = shmget( key_taa, sizeof(No_taa), IPC_CREAT | 0666)) == -1 ) {
		fprintf(stderr,"Impossivel criar o segmento de memoria compartilhada PCB!\n");
		exit(0);	
	}

	if( (novo_addr = shmat(mem_novo, NULL, 0)) == (char *)-1 ) {
		fprintf(stderr,"Impossivel associar o segmento de memoria compartilhada PCB!\n");
		exit(0);
	}

	novo = (No_taa*) novo_addr;


	etaa->prox = novo;
	//etaa->prox = key_taa;
	novo->quant_proc_acessando = 1;
	novo->i_node.criacao= 3;
	novo->i_node.ultima_modificacao= 4;
	novo->i_node.idi_node = 6;
	novo->i_node.tamanho = 4096;
	
		
	for(i=0; i<10; i++)
	{
		novo->i_node.direto[i]= -1;
	}	
	novo->i_node.direto[0]= 250; 
	novo->i_node.direto[1]= 251; 
	novo->i_node.direto[2]= 252; 
	novo->i_node.direto[3]= 253; 
	novo->i_node.single = -1;
	novo->i_node.doubles = -1;
	novo->i_node.triple = -1;
	
	novo->prox = novo->prox;

	
	//Associa a mem�ria compartilhada do PCB
	mem_pcb = shmget( MEM_PCB, sizeof(Pcb), 0);
	pcb_ptr = (Pcb*) shmat(mem_pcb, NULL, 0);	

	pcb_ptr->pid = 1111;
	pcb_ptr->inode_corrente = 3;

	for(i=0; i<10; i++)
	{
	pcb_ptr->vetor_descritores[i].ponteiro_arq = -1;
	pcb_ptr->vetor_descritores[i].laa_ptr = NULL;
	pcb_ptr->vetor_descritores[i].buffer= -1;
	}
	
	pcb_ptr->vetor_descritores[0].ponteiro_arq = 10;
	pcb_ptr->vetor_descritores[0].laa_ptr = novo;
	pcb_ptr->vetor_descritores[0].buffer = key_buffer;
	pcb_ptr->vetor_descritores[0].modo_abertura = 1;
	pcb_ptr->vetor_descritores[0].aberto = 1;

	//Associa a mem�ria compartilhada do Superbloco
	mem_super = shmget( MEM_SUPERBLOCO, sizeof(Superbloco), 0);
	super = (Superbloco*) shmat(mem_super, NULL, 0);

	for(i = 0; i< 184; i++)
	{
	super->clusters_livres[i] = -1;
	}
	
	
	for(i = 0; i< 50; i++)
	{
	super->clusters_danificados[i] = -1;
	}
	
}
